package com.gp.cyg.task;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.gp.cyg.exception.CustomException;
import com.gp.cyg.utils.ThreadPoolUtils;
import com.gp.cyg.vo.CalculateVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.core.util.SystemMillisClock;
import org.jsoup.Connection;
import org.jsoup.HttpStatusException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * TestCyg
 *
 * @author gsp
 * @date 2022/9/8
 */
@Slf4j
@Component
public class CygBuyTask {
    @Autowired
    private ThreadPoolUtils threadPoolUtils;
    @Value("${delayTime}")
    private Long delayTime;
    //先在页面获取到剩余时间，根据剩余时间算出，销售时间，然后，往定时队列里放入两个任务，一个任务首先获取20个formToken存放到集合中，另一个任务到点后，使用这20个formToken循环发起请求。
    @Value("${ngrokUrl}")
    private String ngrokUrl;

    @Value("${spring:server:port}")
    private String port;

    String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36";

    Boolean buyFlag = false;


    public CompletableFuture<Integer> tasksForOne(String proxyUrl, String goodsCode, String cookie, Proxy proxy, Integer gi) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return executeTask(proxyUrl, goodsCode, cookie, proxy, gi);
            } catch (InterruptedException e) {
                log.info("d-InterruptedException>>>>", e);
                return -1;
            }
        }, threadPoolUtils.getThreadPoolExecutor());
    }

    /**
     * @param proxyUrl
     * @param goodsCode
     * @param cookie
     * @param proxyList
     * @param calculateVO
     * @return void
     * @Description 开始购买任务
     * @author gsp
     * @date 2023/5/10
     */
    public Integer beginBuyTask(String proxyUrl, DelayQueue<MyDelay> delayQueue, String goodsCode, String cookie, ConcurrentLinkedQueue<Proxy> proxyList, CalculateVO calculateVO) {
        delayQueue.add(new MyDelay("开始发起购买请求", calculateVO.getTime() + delayTime));//2100
        log.info("--------任务准备执行------");
        try {
            delayQueue.take();
        } catch (InterruptedException e) {
            log.info("延时任务获取异常", e);
        }
        if (StrUtil.isNotBlank(proxyUrl) && !proxyList.isEmpty()) {
            List<CompletableFuture<Integer>> collect = null;
            for (int i = 0; i < 3; i++) {
                collect = proxyList.stream().map(a -> tasksForOne(proxyUrl, goodsCode, cookie, proxyList.poll(), calculateVO.getGi())).collect(Collectors.toList());
            }
            List<CompletableFuture<Integer>> finalCollect = collect;
            CompletableFuture.allOf(collect.toArray(new CompletableFuture[collect.size()]))
                    .thenApply(v -> {
                        Optional<Integer> first = finalCollect.stream().map(CompletableFuture::join).filter(a -> a.equals(-1)).findFirst();
                        return first.get() == -1 ? -1 : 0;
                    });
        } else if (StrUtil.isBlank(proxyUrl)) {
            tasksForOne(proxyUrl, goodsCode, cookie, null, calculateVO.getGi()).join();
            return -1;
        }
        return 0;
    }

    //获取最新代售商品的goodsCode
    public String getLastGoodsCode() {
        String url = "http://cyg.changyou.com/list/filterSortGoods.json";
        String param = "{\n" +
                "  \"filterList\": [\n" +
                "    {\n" +
                "      \"field\": \"goodsType\",\n" +
                "      \"value\": 1,\n" +
                "      \"type\": 1\n" +
                "    },\n" +
                "    {\n" +
                "      \"field\": \"status\",\n" +
                "      \"value\": 2,\n" +
                "      \"type\": 1\n" +
                "    },\n" +
                "    {\n" +
                "      \"field\": \"gameId\",\n" +
                "      \"value\": \"16\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"field\": \"serverId\",\n" +
                "      \"value\": \"104\"\n" +
                "    }\n" +
                "  ],\n" +
                "  \"gameServer\": {\n" +
                "    \"gameId\": \"16\",\n" +
                "    \"groupId\": \"1004\",\n" +
                "    \"platformId\": 1,\n" +
                "    \"serverId\": \"104\",\n" +
                "    \"serverName\": \"侠客岛\",\n" +
                "    \"worldId\": \"电信一区\",\n" +
                "    \"worldName\": \"侠客岛\"\n" +
                "  },\n" +
                "  \"sort\": {\n" +
                "    \"key\": \"offlineTime\",\n" +
                "    \"value\": \"desc\"\n" +
                "  },\n" +
                "  \"pageNumber\": 1,\n" +
                "  \"pageSize\": 241\n" +
                "}";
        String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36";
        try {
            Document document = Jsoup.connect(url)
                    .requestBody(JSONObject.parseObject(param).toJSONString())
                    .userAgent(userAgent)
                    .ignoreContentType(true)
                    .method(Connection.Method.POST)
                    .header("Content-Type", "application/json")
                    .post();
//.m-goods-info http://public.npt.changyou.com/assets/js/page/detail/character.introhelper.js?v=1.1.14
            String response = JSONObject.parseObject(JSONObject.parseObject(document.getElementsByTag("body")
                    .text()).get("data").toString()).get("list").toString();
            int i = response.indexOf("goodsCode\":\"");
            return JSONObject.parseObject(JSONObject.parseObject(document.getElementsByTag("body")
                    .text()).get("data").toString()).get("list").toString()
                    .substring(i + 12, i + 31);
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    //fromToken有过期时间，不能一大早就获取formToken
    public String getFromToken(String url) {
        try {
            Document document = Jsoup.connect(url)
                    .userAgent(userAgent)
                    .ignoreContentType(true)
                    .header("Content-Type", "application/json")
                    .get();
            String script = document.getElementsByTag("script")
                    .toString();
            int j = script.indexOf("window.formToken");
            Thread.sleep(300);
            return script.substring(j + 20, j + 52);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("获取formToken失败");
        }
        return "";
    }

    public String executeConfirmBuy(String cookie, String formToken, String actualGoodsCode, Integer gi) {
        String comfirmUrl = "http://cyg.changyou.com/order/confirmBuy.json?" +
                "gc=%s&gi=%s&qt=%s&gid=%s&formToken=%s";
        HttpResponse execute = HttpUtil.createPost(String.format(comfirmUrl, actualGoodsCode, gi, 1, 16, formToken))
                .header("User-Agent", userAgent)
                .header("Cookie", cookie)
                .header("X-Requested-With", "XMLHttpRequest").execute();
        cn.hutool.json.JSONObject entries = JSONUtil.parseObj(execute.body());
        if (!entries.isEmpty() && entries.get("code") != null) {
            return String.valueOf(entries.get("code"));
        }
        return "";

    }

    /**
     * 获取出剩余时间
     */
    public CalculateVO calculateRemainTime(String goodsCode) {
        String buyingGoodsUrl = "http://cyg.changyou.com/details/?goodsCode=" + goodsCode;
        try {
            Document document = Jsoup.connect(buyingGoodsUrl)
                    .userAgent(userAgent)
                    .ignoreContentType(true)
                    .header("Content-Type", "application/json")
                    .get();
            CalculateVO calculateVO = new CalculateVO();
            calculateVO.setGi(Integer.valueOf(document.getElementsByClass("m-goods-info").attr("data-serverId")));
            calculateVO.setTime(Long.parseLong(document.getElementsByClass("remainTime").attr("data-remain")));
            return calculateVO;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new CalculateVO();
    }

    public void executeBuyByBatchIp() {

    }

    public void getIpByBatch(String proxyUrl, CalculateVO calculateVO, DelayQueue delayQueue, ConcurrentLinkedQueue<Proxy> proxyList, Integer count,Long delayTime) {
            //批量获取代理ip延时任务
            delayQueue.add(new MyDelay("启动批量获取代理IP延时任务", calculateVO.getTime() - Integer.parseInt(count + "000")+delayTime));//2100
            log.info("-----批量获取代理ip准备执行-------");
            try {
                delayQueue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < count; i++) {
                String[] proxy = HttpUtil.createGet(proxyUrl)
                        .execute()
                        .body()
                        .split(":");
                proxyList.add(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy[0], Integer.parseInt(proxy[1]))));
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }


    public Integer executeTask(String proxyUrl, String actualGoodsCode, String cookie, Proxy proxy, Integer gi) throws CustomException, InterruptedException {
        Integer code = 0;
        while (true) {
            try {
                if (proxy != null) {
                    String buyingGoodsUrl = "http://cyg.changyou.com/details/?goodsCode=" + actualGoodsCode;
                    Document document = Jsoup.connect(buyingGoodsUrl)
                            .userAgent(userAgent)
                            .ignoreContentType(true)
                            .header("Content-Type", "application/json")
                            .proxy(proxy)
                            .get();
                    String goodsStatus = document.getElementsByClass("goodsStatus").text();
                    if ("待售中".equals(goodsStatus)) {
                        String script = document.getElementsByTag("script")
                                .toString();
                        int j = script.indexOf("window.formToken");
                        String fromToken = script.substring(j + 20, j + 52);
                        executeConfirmBuy(cookie, fromToken, actualGoodsCode, gi);
                    } else if ("已下单".equals(goodsStatus)) {
                        code = -1;
                        break;
                    }
                } else {
                    String buyingGoodsUrl = "http://cyg.changyou.com/details/?goodsCode=" + actualGoodsCode;
                    Document document = Jsoup.connect(buyingGoodsUrl)
                            .userAgent(userAgent)
                            .ignoreContentType(true)
                            .header("Content-Type", "application/json")
                            .get();
                    String goodsStatus = document.getElementsByClass("goodsStatus").text();
                    if ("待售中".equals(goodsStatus)) {
                        String script = document.getElementsByTag("script")
                                .toString();
                        int j = script.indexOf("window.formToken");
                        String fromToken = script.substring(j + 20, j + 52);
                        executeConfirmBuy(cookie, fromToken, actualGoodsCode, gi);
                    } else if ("已下单".equals(goodsStatus)) {
                        code = -1;
                        break;
                    }
                }
            } catch (IOException | JSONException e) {
                log.info("执行任务异常>>>>", e);
                if (e instanceof HttpStatusException || e instanceof JSONException || e instanceof java.net.ConnectException) {
                    code = 0;
                    break;
                }
            }
        }
        log.info(actualGoodsCode + "：执行完毕,当前线程id>>>>>{}", Thread.currentThread().getId());
        return code;
    }

//    private void createTask(String proxyUrl,String goodsCode,String cookie,Integer gi) {
//        String[] proxy = HttpUtil.createGet(proxyUrl)
//                .execute()
//                .body()
//                .split(":");
//        Proxy actProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy[0], Integer.parseInt(proxy[1])));
//        try {
//            this.executeTask(proxyUrl,goodsCode, cookie, actProxy,gi);
//        } catch (InterruptedException e) {
//            log.info("再次提交任务异常>>>>{}",e.getMessage());
//        }
//    }

    public Document backupTreatmentMethod(String actualGoodsCode) throws IOException, JSONException, HttpStatusException {
        String buyingGoodsUrl = "http://cyg.changyou.com/details/?goodsCode=" + actualGoodsCode;
        Document document = null;
        //Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("107.172.8.35", 52845));
        //Authenticator.setDefault(new MyAuthenticator("233blog", "233blog.com"));
        document = Jsoup.connect(buyingGoodsUrl)//.proxy(proxy)
                .userAgent(userAgent)
                .ignoreContentType(true)
                .header("Content-Type", "application/json")
                .timeout(800)
                .get();
        log.info("后续处理恩物获取formToken和商品状态是否被限流了>>>>>{}", document.getElementsByClass("maintenance"));
        log.info("后续处理任务>>>>{}", document.getElementsByClass("goodsStatus"));
        return document;
    }

   /*public static void main(String[] args) throws Exception {
        URL url = new URL("https://stackoverflow.com/questions/49413900/multithreaded-jsoup-proxy-authentication");
   *//*     SocketAddress proxyAddr = new InetSocketAddress("107.172.8.35", 52845);
        Proxy pr = new Proxy(Proxy.Type.SOCKS, proxyAddr);
   *//**//*     System.getProperties().put( "proxySet", "true" );
        System.getProperties().put( "socksProxyHost", "107.172.8.35" );
        System.getProperties().put( "socksProxyPort", "52845" );*//**//*
        Authenticator.setDefault(new MyAuthenticator("233blog", "233blog.com"));
        HttpURLConnection con = (HttpURLConnection) url.openConnection(pr);
        con.setConnectTimeout(10000);
        con.setReadTimeout(10000);
        con.connect();
        System.out.println(con.usingProxy());*//*
    }*/


    public void checkRemainTime(String goodsCode) throws IOException {
        log.info("delayTime>>>>{}", delayTime);
        String buyingGoodsUrl = "http://cyg.changyou.com/details/?goodsCode=" + goodsCode;
        Document document = Jsoup.connect(buyingGoodsUrl)
                .userAgent(userAgent)
                .ignoreContentType(true)
                .header("Content-Type", "application/json")
                .get();
        if ("已下单".equals(document.getElementsByClass("goodsStatus").text())
                || "已销售".equals(document.getElementsByClass("goodsStatus").text())) {
            throw new CustomException("商品已被购买");
        }
        long remainTime = Long.parseLong(document.getElementsByClass("remainTime").attr("data-remain"));
        log.info("剩余时间>>>>{}分{}秒", DateUtil.date(remainTime).minute(),DateUtil.date(remainTime).second());
//        if (remainTime > 1200000) {
//            throw new CustomException("启动任务的商品剩余时间不要大于20分钟");
//        }
    }

    public void backGroundTask(String actualGoodsCode, String cookie, Integer gi) throws InterruptedException {
        log.info("后续任务》》》");
        while (true) {
            try {
                Document document = backupTreatmentMethod(actualGoodsCode);
                String goodsStatus = document.getElementsByClass("goodsStatus").text();
                if ("待售中".equals(goodsStatus)) {
                    String script = document.getElementsByTag("script")
                            .toString();
                    int j = script.indexOf("window.formToken");
                    String fromToken = script.substring(j + 20, j + 52);
                    executeConfirmBuy(cookie, fromToken, actualGoodsCode, gi);
                } else if ("已下单".equals(goodsStatus)) {
                    break;
                }
                Thread.sleep(200);
            } catch (IOException | JSONException | InterruptedException e) {
                log.info("后置处理任务异常>>>>{}", e.toString());
                if (e instanceof HttpStatusException || e instanceof JSONException) {
                    requestOtherNode(actualGoodsCode, cookie);
                    break;
                }
            }
        }
    }

    public void backGroundFirstTask(DelayQueue backGroundTaskQueue, String actualGoodsCode, String cookie, Integer gi) throws InterruptedException {
        log.info("首次任务》》》");
        backGroundTaskQueue.take();
        while (true) {
            try {
                Document document = backupTreatmentMethod(actualGoodsCode);
                String goodsStatus = document.getElementsByClass("goodsStatus").text();
                if ("待售中".equals(goodsStatus)) {
                    String script = document.getElementsByTag("script")
                            .toString();
                    int j = script.indexOf("window.formToken");
                    String fromToken = script.substring(j + 20, j + 52);
                    executeConfirmBuy(cookie, fromToken, actualGoodsCode, gi);
                } else if ("已下单".equals(goodsStatus)) {
                    break;
                }
                Thread.sleep(200);
            } catch (IOException | JSONException | InterruptedException e) {
                log.info("后置处理任务异常>>>>{}", e.toString());
                if (e instanceof HttpStatusException || e instanceof JSONException) {
                    log.info("开始发起服务器请求");
                    requestOtherNode(actualGoodsCode, cookie);
                    break;
                }
            }
        }
    }

    private void requestOtherNode(String goodsCode, String cookie) {
        String serverIp = "107.172.8.35";
        try {
            String targetUrl = InetAddress.getLocalHost().getHostAddress().equals(serverIp) ? ngrokUrl : serverIp + ":" + "8080";
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("goodsCode", goodsCode);
            jsonObject.put("cookie", cookie);
            jsonObject.put("flag", "false");
            HttpResponse execute = HttpUtil
                    .createPost(targetUrl + "/v1/backupTreatmentMethod").body(jsonObject.toJSONString()).execute();
            log.info("请求OtherNode信息>>>>{}", execute.body());
        } catch (UnknownHostException e) {
            log.info("未知主机异常>>>>{}", e.toString());
        }
    }

    public Integer beginBuyTaskSingleIp(String proxyUrl, DelayQueue<MyDelay> delayQueue, String goodsCode, String cookie,CalculateVO calculateVO,Long delayTime) {
        delayQueue.add(new MyDelay("购买请求延时任务", calculateVO.getTime() + delayTime));//2100
        try {
            delayQueue.take();
        } catch (InterruptedException e) {
            log.info("购买任务提取异常",e);
        }
        tasksForOne(proxyUrl, goodsCode, cookie, null, calculateVO.getGi());
        return -1;
    }

    public Integer beginBuyTaskMuchIp(String proxyUrl, DelayQueue<MyDelay> delayQueue, String goodsCode, String cookie, ConcurrentLinkedQueue<Proxy> proxyList, CalculateVO calculateVO) {
        List<CompletableFuture<Integer>> collect = null;
        for (int i = 0; i < 3; i++) {
            collect = proxyList.stream().map(a -> tasksForOne(proxyUrl, goodsCode, cookie, proxyList.poll(), calculateVO.getGi())).collect(Collectors.toList());
        }
        List<CompletableFuture<Integer>> finalCollect = collect;
        CompletableFuture.allOf(collect.toArray(new CompletableFuture[collect.size()]))
                .thenApply(v -> {
                    Optional<Integer> first = finalCollect.stream().map(CompletableFuture::join).filter(a -> a.equals(-1)).findFirst();
                    return first.get() == -1 ? -1 : 0;
                });
        return 0;
    }
}
